package tech.waterism.utils.method;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.BooleanUtil;
import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import tech.waterism.constant.StationTypeConstans;
import tech.waterism.dto.*;
import tech.waterism.entity.FCurveHq;
import tech.waterism.utils.CommonFun;
import tech.waterism.utils.ConvertZQ;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

@Data
@Component
public class StatisticsCalculate {
    private static final Set<String> DETENTION_SET = new HashSet<>();

    private static final Set<String> ENGINEERING_SET = new HashSet<>();
    private static final Set<String> FLOW_SET = new HashSet<>();

    static {
        FLOW_SET.add("Q");
        FLOW_SET.add("Z");
        FLOW_SET.add("ZQ");
        FLOW_SET.add("IQ");
        FLOW_SET.add("IZQ");
        //
        DETENTION_SET.add("DZQ");
        //
        ENGINEERING_SET.add("IQ");
        ENGINEERING_SET.add("IZQ");
        ENGINEERING_SET.add("DZQ");
    }

    /**
     * 获取 dtOperate 后的（含 dtOperate）入流峰值
     */
    public static Double getQpeak(List<ForecastDataDto> data, Date dtOperate) {
        if (data == null || data.size() == 0) {
            return null;
        }
        DispatchStatisticsDto statistics = new DispatchStatisticsDto();
        data.stream().filter(i -> !i.getDt().before(dtOperate)).filter(i -> i.getQSim() != null)
                .max(Comparator.comparing(ForecastDataDto::getQSim)).ifPresent(i -> {
                    statistics.setQpeak(i.getQSim());
                    statistics.setQpeaktime(i.getDt());
                });
        return statistics.getQpeak();
    }

    /**
     * 预报调度统计计算
     *
     * @param fpIndexDto     预报方案索Dto
     * @param dtOperate      起调时间
     * @param timeSteps      时间步长
     * @param stepUnit       步长单位
     * @param outcd          输出代码
     * @param floodCharacter 防洪特征信息
     */
    public synchronized DispatchStatisticsDto calculate(FPIndexDto fpIndexDto, Date dtOperate, Integer timeSteps,
                                                        String stepUnit, String outcd, FloodCharacterDto floodCharacter, List<FCurveHq> curveHq) {
        // 水位保留两位小数之后比较
        fpIndexDto.getData().forEach(i -> {
            if (i.getRz() != null) {
                BigDecimal bd = BigDecimal.valueOf(i.getRz());
                bd = bd.setScale(2, RoundingMode.HALF_UP);
                i.setRz(bd.doubleValue());
            }
        });
        DispatchStatisticsDto statistics = new DispatchStatisticsDto();
        List<ForecastDataDto> data = fpIndexDto.getData();
        boolean overNormZ = false;
        boolean overFsltdZ = false;

        if (data != null) {
            List<DateInterval> rzoverfsltdztimeList = new ArrayList<>();
            for (ForecastDataDto dataOne : data) {
                if (dataOne.getDt().equals(dtOperate)) {
                    if (ENGINEERING_SET.contains(outcd)) {
                        statistics.setRzbeg(dataOne.getRz());
                        // 水库/蓄滞洪区最高水位初值
                        statistics.setRzpeak(dataOne.getRz());
                        statistics.setRzpeaktime(dataOne.getDt());
                        if (DETENTION_SET.contains(outcd)) {
                            // 分洪洪量初值
                            statistics.setQdvrsum(0.0);
                            // 排洪洪量初值
                            statistics.setQdscsum(0.0);
                        }
                    }
                    if (FLOW_SET.contains(outcd)) {
                        double qreal = 0.0;
                        double zreal = -9999;
                        if (dataOne.getQReal() != null) {
                            qreal = dataOne.getQReal();
                        }
                        if (dataOne.getZReal() != null) {
                            zreal = dataOne.getZReal();
                        }
                        // 水库/河道起调洪峰流量初值
                        // statistics.setQpeak(qreal);
                        // statistics.setQpeaktime(dataOne.getDt());
                        // 洪水总量初值
                        statistics.setQsum(0.0);
                        if (StationTypeConstans.RIVER_SET.contains(outcd)) {
                            statistics.setQbeg(qreal);
                            statistics.setZbeg(zreal);
                            // 河道最高水位初值
                            statistics.setZpeak(zreal);
                            statistics.setZpeaktime(dataOne.getDt());
                        }
                    }
                }
                if (!dataOne.getDt().before(dtOperate)) { // 起调后
                    if (ENGINEERING_SET.contains(outcd)) {
                        // 水库/蓄滞洪区最高水位
                        if (dataOne.getRz() != null && dataOne.getRz() > statistics.getRzpeak()) {
                            statistics.setRzpeak(dataOne.getRz());
                            statistics.setRzpeaktime(dataOne.getDt());
                        }
                        // 蓄滞洪区分洪/排洪洪量
                        if (DETENTION_SET.contains(outcd)) {
                            if (dataOne.getQDvr() != null && dataOne.getQDvr() > 0) {
                                if (statistics.getQdvrbegtime() == null) {
                                    statistics.setQdvrbegtime(dataOne.getDt());
                                }
                                statistics.setQdvrsum(statistics.getQdvrsum() +
                                        dataOne.getQDvr() * CommonFun.multiKFromFlowToVolumn(timeSteps, stepUnit));
                            }
                            if (dataOne.getQDsc() != null && dataOne.getQDsc() > 0) {
                                if (statistics.getQdscbegtime() == null) {
                                    statistics.setQdscbegtime(dataOne.getDt());
                                }
                                statistics.setQdscsum(statistics.getQdscsum() +
                                        dataOne.getQDsc() * CommonFun.multiKFromFlowToVolumn(timeSteps, stepUnit));
                            }
                        }
                        // 正常高水位
                        if (floodCharacter != null && floodCharacter.getStRsvrfcchB() != null &&
                                floodCharacter.getStRsvrfcchB().getNormz() != null) {
                            if (dataOne.getRz() != null) {
                                if (dataOne.getRz() >= floodCharacter.getStRsvrfcchB().getNormz()) {
                                    if (statistics.getRzovernormztime() == null) {
                                        statistics.setRzovernormztime(dataOne.getDt());
                                    }
                                    statistics.setRzbelownormztime(null);
                                    overNormZ = true;
                                }
                                if (overNormZ) {
                                    if (dataOne.getRz() < floodCharacter.getStRsvrfcchB().getNormz()) {
                                        statistics.setRzbelownormztime(dataOne.getDt());
                                        overNormZ = false;
                                    }
                                }
                            }
                        }
                        // 汛限水位
                        if (floodCharacter != null && floodCharacter.getStRsvrfsrBList() != null) {
                            double zSummer = ResFlood.getZNSummer(floodCharacter.getStRsvrfsrBList(), dataOne.getDt());
                            if (dataOne.getRz() != null) {
                                if (zSummer != -1 && dataOne.getRz() >= zSummer) {
                                    if (statistics.getRzoverfsltdztime() == null) {
                                        statistics.setRzoverfsltdztime(dataOne.getDt());
                                    }
                                    if (!overFsltdZ) {
                                        DateInterval dateInterval = new DateInterval();
                                        dateInterval.setStart(dataOne.getDt());
                                        dateInterval.setEnd(dataOne.getDt());
                                        rzoverfsltdztimeList.add(dateInterval);
                                    } else {
                                        if (rzoverfsltdztimeList.size() > 0) {
                                            rzoverfsltdztimeList.get(rzoverfsltdztimeList.size() - 1)
                                                    .setEnd(dataOne.getDt());
                                        }
                                    }
                                    statistics.setRzbelowfsltdztime(null);
                                    overFsltdZ = true;
                                }
                                if (overFsltdZ) {
                                    if (dataOne.getRz() < zSummer) {
                                        statistics.setRzbelowfsltdztime(dataOne.getDt());
                                        overFsltdZ = false;
                                    }
                                }
                                dataOne.setIsOverFsltdZ(overFsltdZ);
                            }
                        }
                    }
                    if (FLOW_SET.contains(outcd)) {
                        // 水库/河道峰值流量 洪量
                        double q = 0.0;
                        if (dataOne.getDt().equals(dtOperate)) {
                            if (dataOne.getQReal() != null) {
                                q = dataOne.getQReal();
                            }
                        } else {
                            if (dataOne.getQSim() != null) {
                                q = dataOne.getQSim();
                            }
                            if (dataOne.getQDvr() != null) {
                                q = q - dataOne.getQDvr();
                            }
                            if (dataOne.getQDsc() != null) {
                                q = q + dataOne.getQDsc();
                            }
                            statistics.setQsum(
                                    statistics.getQsum() + q * CommonFun.multiKFromFlowToVolumn(timeSteps, stepUnit));
                        }
                        dataOne.setQDispatch(q);
                        if (statistics.getQpeak() != null) {
                            if (q > statistics.getQpeak()) {
                                statistics.setQpeak(q);
                                statistics.setQpeaktime(dataOne.getDt());
                            }
                        } else {
                            statistics.setQpeak(q);
                            statistics.setQpeaktime(dataOne.getDt());
                        }
                        if (StationTypeConstans.RIVER_SET.contains(outcd)) {
                            double z = -1;
                            if (dataOne.getDt().equals(dtOperate)) {
                                if (dataOne.getZReal() != null) {
                                    z = dataOne.getZReal();
                                } else {
                                    if (curveHq != null && curveHq.size() > 0) {
                                        z = ConvertZQ.getZFromQ(curveHq, q);
                                    }
                                }
                            } else {
                                if (curveHq != null && curveHq.size() > 0) {
                                    z = ConvertZQ.getZFromQ(curveHq, q);
                                }
                            }
                            if (z > 0) {
                                dataOne.setZDispatch(z);
                            }
                            if (z > statistics.getZpeak()) {
                                statistics.setZpeak(z);
                                statistics.setZpeaktime(dataOne.getDt());
                            }

                            if (floodCharacter != null && floodCharacter.getStRvfcchB() != null) {
                                // 保证水位
                                if (floodCharacter.getStRvfcchB().getGrz() != null) {
                                    if (statistics.getZovergrztime() == null &&
                                            z >= floodCharacter.getStRvfcchB().getGrz()) {
                                        statistics.setZovergrztime(dataOne.getDt());
                                    }
                                }
                                // 保证流量
                                if (floodCharacter.getStRvfcchB().getGrq() != null) {
                                    if (statistics.getQovergrqtime() == null &&
                                            q >= floodCharacter.getStRvfcchB().getGrq()) {
                                        statistics.setQovergrqtime(dataOne.getDt());
                                    }
                                }
                                // 警戒水位
                                if (floodCharacter.getStRvfcchB().getWrz() != null) {
                                    if (statistics.getZoverwrztime() == null &&
                                            z >= floodCharacter.getStRvfcchB().getWrz()) {
                                        statistics.setZoverwrztime(dataOne.getDt());
                                    }
                                }
                                // 警戒流量
                                if (floodCharacter.getStRvfcchB().getWrq() != null) {
                                    if (statistics.getQoverwrqtime() == null &&
                                            q >= floodCharacter.getStRvfcchB().getWrq()) {
                                        statistics.setQoverwrqtime(dataOne.getDt());
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if (rzoverfsltdztimeList.size() > 0) {
                statistics.setRzoverfsltdztimeList(rzoverfsltdztimeList);
            }
            data.stream().filter(i -> !i.getDt().before(dtOperate)).filter(i -> i.getQOut() != null)
                    .max(Comparator.comparing(ForecastDataDto::getQOut)).ifPresent(i -> {
                        statistics.setQoutPeak(i.getQOut());
                        statistics.setQoutPeaktime(i.getDt());
                    });
            double qoutsum = 0d;
            List<ForecastDataDto> forecastDataDtoList =
                    data.stream().filter(i -> !i.getDt().before(dtOperate)).filter(i -> i.getQOut() != null).toList();
            if (CollectionUtils.isNotEmpty(forecastDataDtoList)) {
                for (ForecastDataDto forecastDataDto : forecastDataDtoList) {
                    Double qout = forecastDataDto.getQOut();
                    qoutsum = qoutsum + qout * CommonFun.multiKFromFlowToVolumn(timeSteps, stepUnit);
                }
            }
            statistics.setQoutsum(qoutsum);
            // pre data
            List<ForecastDataDto> nonOrganizedData = fpIndexDto.getDataNonOrganized();
            if (nonOrganizedData != null) {
                overFsltdZ = false;
                List<DateInterval> preRzoverfsltdztimeList = new ArrayList<>();
                for (ForecastDataDto dataOne : nonOrganizedData) {
                    // 汛限水位
                    if (floodCharacter != null && floodCharacter.getStRsvrfsrBList() != null) {
                        double zSummer = ResFlood.getZNSummer(floodCharacter.getStRsvrfsrBList(), dataOne.getDt());
                        if (dataOne.getRz() != null) {
                            if (zSummer != -1 && dataOne.getRz() >= zSummer) {
                                if (statistics.getRzoverfsltdztime() == null) {
                                    statistics.setPreRzoverfsltdztime(dataOne.getDt());
                                }
                                if (!overFsltdZ) {
                                    DateInterval dateInterval = new DateInterval();
                                    dateInterval.setStart(dataOne.getDt());
                                    dateInterval.setEnd(dataOne.getDt());
                                    preRzoverfsltdztimeList.add(dateInterval);
                                } else {
                                    if (preRzoverfsltdztimeList.size() > 0) {
                                        preRzoverfsltdztimeList.get(preRzoverfsltdztimeList.size() - 1)
                                                .setEnd(dataOne.getDt());
                                    }
                                }
                                statistics.setPreRzbelowfsltdztime(null);
                                overFsltdZ = true;
                            }
                            if (overFsltdZ) {
                                if (dataOne.getRz() < zSummer) {
                                    statistics.setPreRzbelowfsltdztime(dataOne.getDt());
                                    overFsltdZ = false;
                                }
                            }
                            dataOne.setIsOverFsltdZ(overFsltdZ);
                        }
                    }
                }
                if (preRzoverfsltdztimeList.size() > 0) {
                    statistics.setPreRzoverfsltdztimeList(preRzoverfsltdztimeList);
                }
                double preQSum = 0;
                List<ForecastDataDto> preQSumList = nonOrganizedData.stream().filter(i -> i.getDt().before(dtOperate))
                        .filter(i -> Objects.nonNull(i.getQReal())).toList();
                for (int i = 0; i < preQSumList.size(); i++) {
                    ForecastDataDto lastForecastDataDto = preQSumList.get(Math.max(i - 1, 0));
                    ForecastDataDto forecastDataDto = preQSumList.get(i);
                    long nonOrganizedTimeSteps =
                            DateUtil.between(lastForecastDataDto.getDt(), forecastDataDto.getDt(), DateUnit.HOUR);
                    if (Objects.nonNull(forecastDataDto.getQReal())) {
                        double q = forecastDataDto.getQReal();
                        preQSum = preQSum + q * CommonFun.multiKFromFlowToVolumn(nonOrganizedTimeSteps, "H");
                    }
                }
                statistics.setPreQSum(preQSum);
                nonOrganizedData.stream().filter(i -> i.getDt().before(dtOperate))
                        .filter(i -> BooleanUtil.isTrue(i.getQRealIsExist()) && i.getQReal() != null)
                        .max(Comparator.comparing(ForecastDataDto::getQReal)).ifPresent(i -> {
                            statistics.setPreQpeak(i.getQReal());
                            statistics.setPreQpeaktime(i.getDt());
                        });
                nonOrganizedData.stream().filter(i -> i.getDt().before(dtOperate))
                        .filter(i -> BooleanUtil.isTrue(i.getZRealIsExist()) && i.getZReal() != null)
                        .max(Comparator.comparing(ForecastDataDto::getZReal)).ifPresent(i -> {
                            statistics.setPreZpeak(i.getZReal());
                            statistics.setPreZpeaktime(i.getDt());
                        });
                if (ENGINEERING_SET.contains(outcd)) {
                    nonOrganizedData.stream().filter(i -> i.getDt().before(dtOperate)).filter(i -> i.getQOut() != null)
                            .max(Comparator.comparing(ForecastDataDto::getQOut)).ifPresent(i -> {
                                statistics.setPreQOutPeak(i.getQOut());
                                statistics.setPreQOutPeakTime(i.getDt());
                            });
                    nonOrganizedData.stream().filter(i -> i.getDt().before(dtOperate)).filter(i -> i.getRz() != null)
                            .max(Comparator.comparing(ForecastDataDto::getRz)).ifPresent(i -> {
                                statistics.setPreRzpeak(i.getRz());
                                statistics.setPreRzpeaktime(i.getDt());
                            });
                }
            }
        }

        return statistics;
    }
}
